import 'dart:math';
import 'dart:typed_data';
import 'dart:ui' as ui;
import 'dart:ui';

import 'package:flutter/material.dart';

class Paper extends StatelessWidget {
  const Paper({super.key});

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      child: CustomPaint(
        painter: PaperPainter(),
      ),
    );
  }
}

class PaperPainter extends CustomPainter {
  final Paint mPaint = Paint()
    ..style = PaintingStyle.fill
    ..color = Colors.blue;
  final Path mPath = Path();
  final List<Offset> points = const [
    Offset(-120, -20),
    Offset(-80, -80),
    Offset(-40, -40),
    Offset(0, -100),
    Offset(40, -140),
    Offset(80, -160),
    Offset(120, -100),
  ];

  @override
  void paint(Canvas canvas, Size size) {
    // 平移变换
    testTranslate(canvas, size);
    // 缩放变换
    testScale(canvas, size);
    // 旋转变换
    testRotate(canvas, size);

    // 绘制点
    //_drawPointsWithPoints(canvas);
    //_drawPointsWithLines(canvas);
    //_drawPointsWithPolygon(canvas);
    _drawRawPoints(canvas);

    // 绘制线
    _drawAxis(canvas, size);

    // 绘制矩形
    //_drawRect(canvas);
    //_drawRRect(canvas);
    // _drawDRRect(canvas);

    // 绘制类圆
    //_drawFill(canvas);

    // 绘制颜色
    //_drawColor(canvas);

    // 绘制画笔
    //_drawPaint(canvas, size);

    // 绘制阴影
    //_drawShadow(canvas);

    // 绘制路径
    //_drawPath(canvas);

    // 矩形裁剪
    _clipRect(canvas);
  }

  /// 测试 平移变换
  void testTranslate(Canvas canvas, Size size) {
    // 如果想要屏幕的 (0,0) 点永久在屏幕中心，可以将画布进行偏移，这样之后的绘制就会以中心为原点。
    canvas.translate(size.width / 2, size.height / 2);
    canvas.drawCircle(const Offset(0, 0), 50, mPaint);
    canvas.drawLine(
        const Offset(20, 20),
        const Offset(50, 50),
        mPaint
          ..color = Colors.red
          ..strokeWidth = 5
          ..strokeCap = StrokeCap.round
          ..style = PaintingStyle.stroke);
  }

  /// 测试 缩放变换
  /// 现在通过变换实现一个圆点在中心的网格
  void testScale(Canvas canvas, Size size) {
    mPaint
      ..style = PaintingStyle.stroke
      ..strokeWidth = .5 // 线宽
      ..color = Colors.grey; // 线颜色
    // [1]. 练习平移操作: 通过线的平移绘制出右下角四分之一网格线
    // [2]. 练习缩放操作: 通过缩放四分之一网格线，绘制出另外四分之三网格线
    // [3]. 了解画布的存储【save】和恢复【restore】用法
    _drawGrid(canvas, size);
  }

  // 绘制原点为中心的右下部分网格
  void _drawBottomRight(Canvas canvas, Size size) {
    const double step = 20; // 小格边长
    // 绘制横线
    // 每次画完后将画布向下移 step 距离
    canvas.save();
    for (int i = 0; i < size.height / 2 / step; i++) {
      canvas.drawLine(const Offset(0, 0), Offset(size.width / 2, 0), mPaint);
      canvas.translate(0, step);
    }
    canvas.restore();

    // 绘制竖线
    // 每次画完后将画布往右移 step 距离
    canvas.save();
    for (int i = 0; i < size.width / 2 / step; i++) {
      canvas.drawLine(const Offset(0, 0), Offset(0, size.height / 2), mPaint);
      canvas.translate(step, 0);
    }
    canvas.restore();
  }

  // 绘制网格
  // 如果是相同或者对称的对象，可以通过缩放进行对称变化。
  // 沿x轴镜像，就相当于canvas.scale(1, -1)；
  // 沿y轴镜像，就相当于canvas.scale(-1, 1)；
  // 沿原点镜像，就相当于canvas.scale(-1, -1)；
  void _drawGrid(Canvas canvas, Size size) {
    // 绘制原点为中心的右下部分网格
    _drawBottomRight(canvas, size);

    // 绘制右上网格
    canvas.save();
    canvas.scale(1, -1); // 沿x轴镜像
    _drawBottomRight(canvas, size);
    canvas.restore();

    // 绘制左下网格
    canvas.save();
    canvas.scale(-1, 1); // 沿y轴镜像
    _drawBottomRight(canvas, size);
    canvas.restore();

    // 绘制左上网格
    canvas.save();
    canvas.scale(-1, -1); // 沿原点镜像
    _drawBottomRight(canvas, size);
    canvas.restore();
  }

  /// 测试 旋转变换
  // 围绕圆绘制一圈的小线
  void testRotate(Canvas canvas, Size size) {
    mPaint
      ..strokeWidth = 5
      ..color = Colors.orangeAccent
      ..style = PaintingStyle.stroke;
    const int count = 12;
    const double step = 2 * pi / 12;

    // 遍历 12 次,每次将画布旋转2 * pi / count弧度，这样就可以绘制一圈的小线
    canvas.save();
    for (int i = 0; i < count; i++) {
      canvas.drawLine(const Offset(80, 0), const Offset(100, 0), mPaint);
      canvas.rotate(step);
    }
    canvas.restore();
  }

  // 绘制点：点模式
  void _drawPointsWithPoints(Canvas canvas) {
    _setPointPaint();
    canvas.drawPoints(PointMode.points, points, mPaint);
  }

  // 绘制点：线段模式
  void _drawPointsWithLines(Canvas canvas) {
    _setPointPaint();
    canvas.drawPoints(PointMode.lines, points, mPaint);
  }

  // 绘制点：多边形连线模式
  void _drawPointsWithPolygon(Canvas canvas) {
    _setPointPaint();
    canvas.drawPoints(PointMode.polygon, points, mPaint);
  }

  // 绘制点集
  void _drawRawPoints(Canvas canvas) {
    Float32List pos = Float32List.fromList([
      -120,
      -20,
      -80,
      -80,
      -40,
      -40,
      0,
      -100,
      40,
      -140,
      80,
      -160,
      120,
      -100
    ]);

    _setPointPaint();
    canvas.drawRawPoints(PointMode.points, pos, mPaint);
  }

  // 绘制线
  // 绘制蓝色坐标
  void _drawAxis(Canvas canvas, Size size) {
    mPaint
      ..color = Colors.blue
      ..strokeWidth = 1.5;

    canvas.drawLine(
        Offset(-size.width / 2, 0), Offset(size.width / 2, 0), mPaint);
    canvas.drawLine(
        Offset(0, -size.height / 2), Offset(0, size.height / 2), mPaint);
    // 画坐标箭头
    canvas.drawLine(
        Offset(size.width / 2 - 10, 7), Offset(size.width / 2, 0), mPaint);
    canvas.drawLine(
        Offset(size.width / 2 - 10, -7), Offset(size.width / 2, 0), mPaint);
    canvas.drawLine(
        Offset(7, size.height / 2 - 10), Offset(0, size.height / 2), mPaint);
    canvas.drawLine(
        Offset(-7, size.height / 2 - 10), Offset(0, size.height / 2), mPaint);
  }

  // 绘制矩形
  void _drawRect(Canvas canvas) {
    mPaint
      ..style = PaintingStyle.fill
      ..color = Colors.blue
      ..strokeWidth = 1.5;
    //【1】.矩形中心构造
    Rect rectFromCenter =
        Rect.fromCenter(center: const Offset(0, 0), width: 160, height: 160);
    canvas.drawRect(rectFromCenter, mPaint);
    //【2】.矩形左上右下构造
    Rect rectFromLTRB = const Rect.fromLTRB(-120, -120, -80, -80);
    canvas.drawRect(rectFromLTRB, mPaint);
    //【3】. 矩形左上宽高构造
    Rect rectFromLTWH = const Rect.fromLTWH(80, -120, 40, 40);
    canvas.drawRect(rectFromLTWH, mPaint..color = Colors.red);
    //【4】. 矩形内切圆构造
    Rect rectFromCircle =
        Rect.fromCircle(center: const Offset(100, 100), radius: 20);
    canvas.drawRect(rectFromCircle, mPaint..color = Colors.green);
    //【5】. 矩形两点构造
    Rect rectFromPoints =
        Rect.fromPoints(const Offset(-120, 80), const Offset(-80, 120));
    canvas.drawRect(rectFromPoints, mPaint..color = Colors.purple);
  }

  // 绘制圆角矩形
  void _drawRRect(Canvas canvas) {
    mPaint
      ..style = PaintingStyle.fill
      ..color = Colors.blue
      ..strokeWidth = 1.5;
    //【1】.圆角矩形fromRectXY构造
    Rect rectFromCenter =
        Rect.fromCenter(center: const Offset(0, 0), width: 160, height: 160);
    canvas.drawRRect(RRect.fromRectXY(rectFromCenter, 40, 20), mPaint);
    //【2】.圆角矩形fromLTRBXY构造
    canvas.drawRRect(const RRect.fromLTRBXY(-120, -120, -80, -80, 10, 10),
        mPaint..color = Colors.red);
    //【3】. 圆角矩形fromLTRBR构造
    canvas.drawRRect(
        RRect.fromLTRBR(80, -120, 120, -80, const Radius.circular(10)),
        mPaint..color = Colors.orange);
    //【4】. 圆角矩形fromLTRBAndCorners构造
    canvas.drawRRect(
        RRect.fromLTRBAndCorners(80, 80, 120, 120,
            bottomRight: const Radius.elliptical(10, 10)),
        mPaint..color = Colors.green);
    //【5】. 矩形两点构造
    Rect rectFromPoints =
        Rect.fromPoints(const Offset(-120, 80), const Offset(-80, 120));
    canvas.drawRRect(
        RRect.fromRectAndCorners(rectFromPoints,
            bottomLeft: const Radius.elliptical(10, 10)),
        mPaint..color = Colors.purple);
  }

  // 绘制两个圆角矩形差域
  // 后者的区域必须小于前者
  void _drawDRRect(Canvas canvas) {
    mPaint
      ..style = PaintingStyle.fill
      ..color = Colors.blue
      ..strokeWidth = 1.5;
    Rect outRect =
        Rect.fromCenter(center: const Offset(0, 0), width: 160, height: 160);
    Rect inRect =
        Rect.fromCenter(center: const Offset(0, 0), width: 100, height: 100);
    canvas.drawDRRect(RRect.fromRectXY(outRect, 20, 20),
        RRect.fromRectXY(inRect, 20, 20), mPaint);
  }

  // 绘制类圆：圆、椭圆、圆弧
  void _drawFill(Canvas canvas) {
    canvas.save();
    canvas.translate(-200, 0);
    canvas.drawCircle(const Offset(0, 0), 60, mPaint);
    canvas.restore();

    var rect =
        Rect.fromCenter(center: const Offset(0, 0), height: 100, width: 120);
    canvas.drawOval(rect, mPaint);

    canvas.save();
    canvas.translate(200, 0);
    //drawArc(矩形区域,起始弧度,扫描弧度,是否连中心,画笔)
    canvas.drawArc(rect, 0, pi / 2 * 3, true, mPaint);
    canvas.restore();

    canvas.save();
    canvas.translate(300, 0);
    canvas.drawArc(rect, pi / 2 / 3, pi / 2 * 3 + pi / 2 / 3, true,
        mPaint..color = Colors.yellow);
    canvas.restore();
  }

  // 绘制颜色
  void _drawColor(Canvas canvas) {
    canvas.drawColor(Colors.blue, BlendMode.lighten);
  }

  // 绘制画笔
  void _drawPaint(Canvas canvas, Size size) {
    var colors = const [
      Color(0xFFF60C0C),
      Color(0xFFF3B913),
      Color(0xFFE7F716),
      Color(0xFF3DF30B),
      Color(0xFF0DF6EF),
      Color(0xFF0829FB),
      Color(0xFFB709F4),
    ];
    var pos = [1.0 / 7, 2.0 / 7, 3.0 / 7, 4.0 / 7, 5.0 / 7, 6.0 / 7, 1.0];
    mPaint.shader = ui.Gradient.linear(
        const Offset(0, 0), Offset(size.width, 0), colors, pos, TileMode.clamp);
    mPaint.blendMode = BlendMode.lighten;
    canvas.drawPaint(mPaint);
  }

  // 绘制阴影
  void _drawShadow(Canvas canvas) {
    mPath.reset();
    mPath.lineTo(80, -80);
    mPath.lineTo(-80, 80);
    mPath.close();
    canvas.drawShadow(mPath, Colors.red, 3, true);
    canvas.translate(200, 0);
    canvas.drawShadow(mPath, Colors.red, 3, false);
  }

  // 绘制路径
  void _drawPath(Canvas canvas) {
    mPath.reset();
    mPath.lineTo(60, 60);
    mPath.lineTo(-60, 60);
    mPath.lineTo(60, -60);
    mPath.lineTo(-60, -60);
    mPath.close();
    canvas.drawPath(mPath, mPaint..style = PaintingStyle.fill);
    canvas.translate(140, 0);
    canvas.drawPath(
        mPath,
        mPaint
          ..style = PaintingStyle.stroke
          ..strokeWidth = 2);
  }

  // 矩形裁剪
  void _clipRect(Canvas canvas) {
    var rect = Rect.fromCenter(center: Offset.zero, width: 360, height: 240);
    //canvas.clipRect(rect, doAntiAlias: true, clipOp: ClipOp.intersect);

    // 圆角矩形
    var rRect = Rect.fromCenter(center: Offset.zero,width: 200,height: 100);
    //canvas.clipRRect(RRect.fromRectAndRadius(rRect, const Radius.circular(30)));

    // 路径裁剪
    mPath.reset();
    mPath.lineTo(80, 80);
    mPath.lineTo(-80, 80);
    mPath.close();
    canvas.clipPath(mPath);

    canvas.drawColor(Colors.red, BlendMode.darken);
  }

  void _setPointPaint() {
    mPaint
      ..color = Colors.red
      ..style = PaintingStyle.stroke
      ..strokeWidth = 10
      ..strokeCap = StrokeCap.round;
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) => false;
}
